#include "StdAfx.h"
#include <windowsx.h>
#include <ctime>
#include <sstream>
#include <iomanip>

namespace ui
{
	//CDateTimeUI::m_nDTUpdateFlag
#define DT_NONE   0
#define DT_UPDATE 1
#define DT_DELETE 2
#define DT_KEEP   3

	class CDateTimeWnd : public Window
	{
	public:
		CDateTimeWnd();

		void Init(DateTime* pOwner);
		RECT CalPos();

		virtual std::wstring GetWindowClassName() const override;
		virtual std::wstring GetSuperClassName() const override;
		void OnFinalMessage(HWND hWnd);

		LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
	protected:
		DateTime* m_pOwner;
		HBRUSH m_hBkBrush;
		bool m_bInit;
		bool m_bDropOpen;
		SYSTEMTIME m_oldSysTime;
	};

	CDateTimeWnd::CDateTimeWnd() : m_pOwner(NULL), m_hBkBrush(NULL), m_bInit(false), m_bDropOpen(false)
	{
	}

	void CDateTimeWnd::Init(DateTime* pOwner)
	{
		m_pOwner = pOwner;
		m_pOwner->m_nDTUpdateFlag = DT_NONE;

		if (m_hWnd == NULL)
		{
			RECT rcPos = CalPos();
			UINT uStyle = WS_POPUP;
			POINT pt1 = { rcPos.left, rcPos.top };
			POINT pt2 = { rcPos.right, rcPos.bottom };
			::ClientToScreen(pOwner->GetWindow()->GetHWND(), &pt1);
			::ClientToScreen(pOwner->GetWindow()->GetHWND(), &pt2);
			Create(m_pOwner->GetWindow()->GetHWND(), NULL, uStyle, 0, false, { pt1.x, pt1.y, pt2.x, pt2.y});
			
			SetWindowFont(m_hWnd, ui::GlobalManager::GetFontInfo(L"system_12", m_pOwner->GetWindow()->GetPaintDC())->hFont, TRUE);
		}

#if 0
		if (m_pOwner->GetText().empty()) {
			::GetLocalTime(&m_pOwner->m_sysTime);
		}
		memcpy(&m_oldSysTime, &m_pOwner->m_sysTime, sizeof(SYSTEMTIME));
		::SendMessage(m_hWnd, DTM_SETSYSTEMTIME, 0, (LPARAM)&m_pOwner->m_sysTime);
#else
		if (m_pOwner->IsValidTime()) {
			memcpy(&m_oldSysTime, &m_pOwner->m_sysTime, sizeof(SYSTEMTIME));
		}
		else
		{
			::GetLocalTime(&m_oldSysTime);
		}

		::SendMessage(m_hWnd, DTM_SETSYSTEMTIME, 0, (LPARAM)&m_oldSysTime);
#endif
		::ShowWindow(m_hWnd, SW_SHOW);
		::SetFocus(m_hWnd);

		HWND hWndParent = m_hWnd;
		while (::GetParent(hWndParent) != NULL)
			hWndParent = ::GetParent(hWndParent);
		::SendMessage(hWndParent, WM_NCACTIVATE, TRUE, 0L);

		m_bInit = true;    
	}

	RECT CDateTimeWnd::CalPos()
	{
		UiRect rcPos = m_pOwner->GetPos();

		Control* pParent = m_pOwner;
		RECT rcParent;
		while( pParent = pParent->GetParent() ) {
			if( !pParent->IsVisible() ) {
				rcPos.left = rcPos.top = rcPos.right = rcPos.bottom = 0;
				break;
			}
			rcParent = pParent->GetPos();
			if( !::IntersectRect(&rcPos, &rcPos, &rcParent) ) {
				rcPos.left = rcPos.top = rcPos.right = rcPos.bottom = 0;
				break;
			}
		}

		return rcPos;
	}

	std::wstring CDateTimeWnd::GetWindowClassName() const
	{
		return L"DateTimeWnd";
	}

	std::wstring CDateTimeWnd::GetSuperClassName() const
	{
		return DATETIMEPICK_CLASS;
	}

	void CDateTimeWnd::OnFinalMessage(HWND hWnd)
	{
		if( m_hBkBrush != NULL ) ::DeleteObject(m_hBkBrush);
		//m_pOwner->GetManager()->RemoveNativeWindow(hWnd);
		m_pOwner->m_pDateWindow = NULL;
		delete this;
	}

	LRESULT CDateTimeWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
	{
		LRESULT lRes = 0;
		BOOL bHandled = TRUE;
		if( uMsg == WM_CREATE ) {
			//m_pOwner->GetManager()->AddNativeWindow(m_pOwner, m_hWnd);
			bHandled = FALSE;
		}
		else if (uMsg == WM_KEYDOWN && wParam == VK_ESCAPE)
		{
			m_pOwner->SetTime(&m_oldSysTime);
			/*memcpy(&m_pOwner->m_sysTime, &m_oldSysTime, sizeof(SYSTEMTIME));
			m_pOwner->m_nDTUpdateFlag = DT_UPDATE;
			m_pOwner->UpdateText();*/
			PostMessage(WM_CLOSE);
			return lRes;
		}
		else if(uMsg == OCM_NOTIFY)
		{
			NMHDR* pHeader=(NMHDR*)lParam;
			if(pHeader != NULL && pHeader->hwndFrom == m_hWnd) {
				if(pHeader->code == DTN_DATETIMECHANGE) {
					LPNMDATETIMECHANGE lpChage=(LPNMDATETIMECHANGE)lParam;
					SYSTEMTIME systime;
					::SendMessage(m_hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&systime);
					/*m_pOwner->m_nDTUpdateFlag = DT_UPDATE;
					m_pOwner->UpdateText();*/
					m_pOwner->SetTime(&systime);
				}
				else if(pHeader->code == DTN_DROPDOWN) {
					m_bDropOpen = true;

				}
				else if(pHeader->code == DTN_CLOSEUP) {
					SYSTEMTIME systime;
					::SendMessage(m_hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&systime);
					/*m_pOwner->m_nDTUpdateFlag = DT_UPDATE;
					m_pOwner->UpdateText();*/
					m_pOwner->SetTime(&systime);
					PostMessage(WM_CLOSE);
					m_bDropOpen = false;
				}
			}
			bHandled = FALSE;
		}
		else if(uMsg == WM_KILLFOCUS)
		{
			if(!m_bDropOpen) {
				PostMessage(WM_CLOSE);
			}
			bHandled = FALSE;
		}
		
		else bHandled = FALSE;
		if( !bHandled ) 
			//return __super::HandleMessage(uMsg, wParam, lParam);
			return ::CallWindowProc(m_OldWndProc, m_hWnd, uMsg, wParam, lParam);
		return lRes;
	}
	//////////////////////////////////////////////////////////////////////////
	//

	DateTime::DateTime()
	{
		m_sFormat = "%Y-%m-%d";
		m_sDateTimeFormat = "yyyy-MM-dd";

		SetAttribute(L"bordersize", L"1");
		SetAttribute(L"bordercolor", L"gray");
		SetAttribute(L"align", L"vcenter");

		memset(&m_sysTime, 0, sizeof(m_sysTime));
		m_bReadOnly = false;
		m_pDateWindow = NULL;
		m_nDTUpdateFlag = DT_NONE;
	}

	void DateTime::InitLocalTime()
	{
		SYSTEMTIME st;
		::GetLocalTime(&st);
		SetTime(&st);
	}

	void DateTime::Clear()
	{
		SYSTEMTIME st;
		memset(&st, 0, sizeof(st));
		SetTime(&st);
		m_nDTUpdateFlag = DT_NONE;
	}

	SYSTEMTIME& DateTime::GetTime()
	{
		return m_sysTime;
	}

	void DateTime::SetTime(SYSTEMTIME* pst)
	{
		if (IsEqual(*pst))
			return;
		m_sysTime = *pst;
		Invalidate();
		m_nDTUpdateFlag = DT_UPDATE;
		UpdateText();
		m_nDTUpdateFlag = DT_NONE;

		if (OnEvent.find(kEventValueChange) != OnEvent.cend())
		{
			ui::EventArgs args;
			args.pSender = this;
			args.Type = kEventValueChange;
			OnEvent.find(kEventValueChange)->second(&args);
		}
	}

	bool DateTime::IsEqual(SYSTEMTIME st)
	{
		if (m_sysTime.wYear == st.wYear && m_sysTime.wMonth == st.wMonth && m_sysTime.wDay == st.wDay&& m_sysTime.wHour == st.wHour&& m_sysTime.wMinute == st.wMinute && m_sysTime.wSecond == st.wSecond && m_sysTime.wMilliseconds == st.wMilliseconds)
			return true;
		return false;
	}

	void DateTime::SetReadOnly(bool bReadOnly)
	{
		m_bReadOnly = bReadOnly;
		Invalidate();
	}

	bool DateTime::IsReadOnly() const
	{
		return m_bReadOnly;
	}

	bool DateTime::IsValidTime()
	{
		if (m_sysTime.wYear == 0 && m_sysTime.wMonth == 0 && m_sysTime.wDay == 0 && m_sysTime.wHour == 0 && m_sysTime.wMinute == 0 && m_sysTime.wSecond == 0 && m_sysTime.wMilliseconds == 0)
		{
			return false;
		}
		return true;
	}

	void DateTime::UpdateText()
	{
		if (m_nDTUpdateFlag == DT_DELETE || !IsValidTime()) {
			SetText(_T(""));
		}
		else if (m_nDTUpdateFlag == DT_UPDATE) {
			std::tm tmSystemDate;
			tmSystemDate.tm_sec = m_sysTime.wSecond;
			tmSystemDate.tm_min = m_sysTime.wMinute;
			tmSystemDate.tm_hour = m_sysTime.wHour;
			tmSystemDate.tm_mday = m_sysTime.wDay;
			tmSystemDate.tm_mon = m_sysTime.wMonth - 1;
			tmSystemDate.tm_year = m_sysTime.wYear - 1900;
			tmSystemDate.tm_isdst = -1;

			std::stringstream ss;
			ss << std::put_time(&tmSystemDate, m_sFormat.c_str());
			std::wstring Out;
			StringHelper::MBCSToUnicode(ss.str(), Out, CP_UTF8);
			SetText(Out);
		}
	}

	void DateTime::SetAttribute(const std::wstring& strName, const std::wstring& strValue)
	{
		if (strName == L"format") SetFormat(strValue);
		else if (strName == L"datetimeformat") SetDateTimeFormat(strValue);
		else __super::SetAttribute(strName, strValue);
	}

	const std::string& DateTime::GetFormat() const
	{
		return m_sFormat;
	}

	void DateTime::SetFormat(std::wstring val)
	{
		std::string v;
		StringHelper::UnicodeToMBCS(val, v, CP_UTF8);
		if (m_sFormat == v) return;
		m_sFormat = v;
		Invalidate();
		m_nDTUpdateFlag = DT_UPDATE;
		UpdateText();
		m_nDTUpdateFlag = DT_NONE;
	}

	const std::string& DateTime::GetDateTimeFormat() const
	{
		return m_sDateTimeFormat;
	}

	void DateTime::SetDateTimeFormat(std::wstring val)
	{
		std::string v;
		StringHelper::UnicodeToMBCS(val, v, CP_UTF8);
		m_sDateTimeFormat = v;
	}

	void DateTime::HandleMessage(EventArgs& event)
	{
		if (!this->IsMouseEnabled() && event.Type > kEventMouseBegin && event.Type < kEventMouseEnd) {
			if (this->m_pParent != NULL) this->m_pParent->HandleMessageTemplate(event);
			else __super::HandleMessage(event);
			return;
		}

		if( event.Type == kEventSetCursor && IsEnabled() )
		{
			::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM)));
			return;
		}
		if( event.Type == kEventWindowSize )
		{
			if (m_pDateWindow != NULL) m_pDateWindow->SetFocusNeeded(this);
		}
		if( event.Type == kEventScrollChange )
		{
			if (m_pDateWindow != NULL) return;
		}
		if( event.Type == kEventSetFocus && IsEnabled() ) 
		{
			if (m_pDateWindow) return;
			m_pDateWindow = new CDateTimeWnd();
			ASSERT(m_pDateWindow);
			m_pDateWindow->Init(this);
			m_pDateWindow->ShowWindow();
		}
		if (event.Type == kEventKillFocus && IsEnabled())
		{
			Invalidate();
		}
		if (event.Type == kEventMouseButtonDown || event.Type == kEventInternalDoubleClick || event.Type == kEventMouseRightButtonDown)
		{
			if( IsEnabled() ) {
				m_pWindow->ReleaseCapture();
				if (IsFocused() && m_pDateWindow == NULL)
				{
					m_pDateWindow = new CDateTimeWnd();
					ASSERT(m_pDateWindow);
				}
				if (m_pDateWindow != NULL)
				{
					m_pDateWindow->Init(this);
					m_pDateWindow->ShowWindow();
				}
			}
			return;
		}
		if (event.Type == kEventMouseMove)
		{
			return;
		}
		if (event.Type == kEventMouseButtonUp)
		{
			return;
		}
		if (event.Type == kEventMouseMenu)
		{
			return;
		}
		if (event.Type == kEventMouseEnter)
		{
			return;
		}
		if (event.Type == kEventMouseLeave)
		{
			return;
		}

		__super::HandleMessage(event);
	}
}
